package org.wattdepot.cli;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Locale;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.client.WattDepotClientException;

/**
 * Command line interface designed for the WattDepotClient.
 * 
 * @author Dean Kim
 */
public class CommandLineInterface {

  private static final String EXIT_CODE = "Exit";
  private static final String WATTDEPOT_SERVER = "http://server.wattdepot.org:8182/wattdepot/";
  private static final String USAGE_ERROR = "usage: java -jar wattdepotclient.jar {WattDepotURL}";
  private static final String INVALID_URI = "Invalid WattDepotURI. Exiting.";
  private static final String MAIN_COMMAND_ERROR = "Invalid command. Type Help for a list"
      + " of commands.";
  protected static final String HELP_MESSAGE = "help\n  This Message\nquit\n  Exists the system\n"
      + "sources\n"
      + "  Prints a table summarizing the public sources in the system, one per line.\n"
      + "source {source} summary\n" + "  Prints information about a single source.\n"
      + "list sensordata {source} timestamp {timestamp}\n"
      + "  Prints information about a single sensor data instance.\n"
      + "list sensordata {source} day {day}\n"
      + "  Prints a table listing all of the sensor data instances for the specified day.\n"
      + "list powerGenerated {source} timestamp {timestamp}\n"
      + "  Prints the power generated by the source at the given timestamp.\n"
      + "list powerGenerated {source} day {day} sampling-interval {minutes} statistic "
      + "{max|min|average}\n"
      + "  Returns the maximum, minimum, or average power generated by the source for the day.\n"
      + "total [carbon|energy] {source} day {day} sampling-interval {minutes}\n"
      + "  Returns the total amount of carbon (or energy) emitted by this source for the given "
      + "day.\n"
      + "chart power [generated|consumed] {source} {startday} {endday} sampling-interval "
      + "{minutes} file {file}\n"
      + "  Writes out an html file that will display a chart illustrating the power for the "
      + "source over the interval.";

  private WattDepotClient client;
  private ListCommandCli listCommand;
  private ChartPowerCommandCli chartPower;
  private FuelTypesCommandCli fuelTypesCommand;
  private CarbonContent carbonContentCommand;
  private ListSourceCommandCli listSourceCommand;
  private ListTotalCommandCli listTotalCommand;
  private EnergyStatistics energyStatistics;
  private static boolean debugMode = false;

  /**
   * Valid command list.
   * 
   * @author Dean Kim
   * 
   */
  private enum Command {
    HELP, QUIT, LIST, CHART, FUELTYPES, CARBONCONTENT, SOURCES, SOURCE, TOTAL, STATISTICS
  }

  /**
   * Constructor for the CommandLineInterface class.
   * 
   * @param hostUri String.
   */
  public CommandLineInterface(String hostUri) {
    super();
    this.listCommand = new ListCommandCli();
    this.chartPower = new ChartPowerCommandCli();
    this.fuelTypesCommand = new FuelTypesCommandCli();
    this.client = new WattDepotClient(hostUri);
    this.carbonContentCommand = new CarbonContent();
    this.listSourceCommand = new ListSourceCommandCli();
    this.listTotalCommand = new ListTotalCommandCli();
    this.energyStatistics = new EnergyStatistics();
  }

  /**
   * Getter method for WattDepotClient.
   * 
   * @return WattDepotClient client.
   */
  protected WattDepotClient getClient() {
    return client;
  }

  /**
   * Setter method for WattDepotClient.
   * 
   * @param client WattDepotClient.
   */
  protected void setClient(WattDepotClient client) {
    this.client = client;
  }

  /**
   * Verifies that the the URI provided is a valid Watt Depot server.
   * 
   * @param input String value of URI.
   * @return If URI is valid Watt Depot server, boolean true is returned, otherwise false is
   * returned.
   * 
   */
  protected static boolean isValidWattDepotServer(String input) {

    boolean validate = false;

    if (input.equals(WATTDEPOT_SERVER)) {
      validate = true;
    }

    return validate;
  }

  /**
   * Verifies server connectivity to the watt depot server.
   * 
   * @return True if health check to the server passed. Otherwise, false is passed.
   */
  protected boolean verifyServerConnectivity() {

    return (this.getClient().isHealthy());
  }

  /**
   * Verifies that the command is valid.
   * 
   * @param inputCommand Command to verify.
   * @return If the command is valid, boolean true is returned, otherwise false is returned.
   */
  protected boolean isValidCommand(String inputCommand) {
    boolean validCommand = false;
    Command[] validCommands = Command.values();

    for (Command c : validCommands) {
      if (c.name().equals(inputCommand)) {
        validCommand = true;
      }
    }

    return validCommand;

  }

  /**
   * Processes command line input. Verifies server connectivity. connectivityAvailable = true; }
   * 
   * return connectivityAvailable;
   * 
   * @param args Tokenized command line arguments.
   * @return Results of running the command.
   */
  protected String processUserInput(String[] args) {

    String returnString = "Exiting.";
    BufferedReader consoleInput = new BufferedReader(new InputStreamReader(System.in));

    if (this.verifyServerConnectivity()) {

      while (true) {

        String caseSensitiveUserInput = null;
        String userInput = null;
        String processReturn = null;
        System.out.print(">");

        try {
          caseSensitiveUserInput = consoleInput.readLine();
          if (!(caseSensitiveUserInput == null)) {
            userInput = caseSensitiveUserInput.toUpperCase(Locale.US);
          } // end if
        } // end try
        catch (IOException e) {
          if (CommandLineInterface.debugMode) {
            System.out.println("IOExcpetion: " + e);
          }
        } // end catch

        if (!(userInput == null)) {
          processReturn = this.processMainCommand(userInput);
          if (processReturn.equals(EXIT_CODE)) {
            break;
          }
          else {
            System.out.println(processReturn);
          }
        } // end if (!(userInput == null))
      } // end while (true)
    }
    else {
      returnString = "There was a network error. " + args[0] + " is not accessible. " + "Exiting.";
    }

    return returnString;

  }

  /**
   * Verifies command and calls respective methods for valid command.
   * 
   * @param inputCommand Command to run.
   * @return Output of invalid and valid commands.
   */
  protected String processMainCommand(String inputCommand) {
    // changed from null to empty string to avoid null pointers
    // when the user enters a non-standard command.
    String returnString = "";
    String[] splitCommand = inputCommand.toUpperCase(Locale.US).split(" ");
    if (this.isValidCommand(splitCommand[0])) {
      switch (Command.valueOf(splitCommand[0])) {

      case HELP:
        returnString = getHelpMessage();
        break;
      case QUIT:
        returnString = "Exit";
        break;
      case LIST:
        try {
          returnString = listCommand.processCommand(this.client, splitCommand);
        }
        catch (WattDepotClientException e) {
          // TODO Auto-generated catch block
          if (debugMode) {
            e.printStackTrace();
          }
        } // end catch (WattDepotClientException e)
        break;

      case SOURCES:
        try {
          returnString = this.listSourceCommand.processCommand(this.client, splitCommand);
        }
        catch (WattDepotClientException e) {
          // TODO Auto-generated catch block
          if (debugMode) {
            e.printStackTrace();
          }
        } // end catch (WattDepotClientException e)
        break;

      case SOURCE:
        try {
          returnString = this.listSourceCommand.processCommand(this.client, splitCommand);
        }
        catch (WattDepotClientException e) {
          // TODO Auto-generated catch block
          if (debugMode) {
            e.printStackTrace();
          }
        } // end catch (WattDepotClientException e)
        break;

      case TOTAL:
        try {
          returnString = this.listTotalCommand.processCommand(this.client, splitCommand);
        }
        catch (WattDepotClientException e) {
          // TODO Auto-generated catch block
          if (debugMode) {
            e.printStackTrace();
          }
        } // end catch (WattDepotClientException e)
        break;

      case CHART:
        try {
          returnString = chartPower.processCommand(this.client, splitCommand);
        }
        catch (WattDepotClientException e) {
          if (debugMode) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          } // end catch (WattDepotClientException e)
        }
        catch (IOException e) {
          if (debugMode) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          } // end catch (WattDepotClientException e)
        } // end catch (IOException e)
        break;
      case FUELTYPES:
        try {
          returnString = fuelTypesCommand.processCommand(this.client, splitCommand);
        }
        catch (WattDepotClientException e) {
          if (debugMode) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          } // end catch (WattDepotClientException e)
        } // end case FUELTYPES:
        break;
      case CARBONCONTENT:
        returnString = carbonContentCommand.processCommand(this.client, splitCommand);
        break;
      case STATISTICS:
        returnString = this.energyStatistics.processCommand(this.client);
        break;
      default:
        returnString = CommandLineInterface.MAIN_COMMAND_ERROR;
        break;
      }
    }
    else {
      returnString = CommandLineInterface.MAIN_COMMAND_ERROR;
    }

    return returnString;
  }

  /**
   * Provides the user help message.
   * 
   * @return Help message.
   */
  private String getHelpMessage() {

    String helpMessage;

    helpMessage = CommandLineInterface.HELP_MESSAGE;
    return helpMessage;
  }

  /**
   * Main method.
   * 
   * @param args User arguments. Expecting single argument for the Watt Depot Server URI.
   */
  public static void main(String[] args) {
    // TODO Auto-generated method stub

    CommandLineInterface cli;
    // Console cliConsole = System.console();

    if (!((args.length > 0) && (args.length < 3))) {
      System.out.println(CommandLineInterface.USAGE_ERROR);
      System.exit(0);
    }

    if ((args.length == 2) && (args[1].equals("debugMode"))) {
      CommandLineInterface.debugMode = true;
    }

    if (CommandLineInterface.isValidWattDepotServer(args[0])) {
      cli = new CommandLineInterface(args[0]);
      System.out.println(cli.processUserInput(args));
    }
    else {
      System.out.println("Invalid WattDepotURL. Exiting.");
      System.exit(0);
    }

  } // end main

}
